package com.tools.common.object.json;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.tools.common.container.AbsOwnList;
import com.tools.common.container.AbsOwnSet;
import com.tools.common.container.OwnPairs;
import com.tools.common.container.OwnRows;
import com.tools.common.exception.IORuntimeException;
import com.tools.common.exception.ParseRuntimeException;
import com.tools.common.io.ProfileKit;
import com.tools.common.object.*;
import com.tools.common.object.type.handler.ListTypeHandler;
import com.tools.common.object.type.handler.MapTypeHandler;
import com.tools.common.object.type.handler.PairTypeHandler;
import com.tools.common.object.type.handler.SetTypeHandler;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.net.URL;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 封装 Jackson 的一些简单的工具类方法
 * */
@Note("封装 Jackson 的一些简单的工具类方法")
class JacksonKit {

    JacksonKit() {}

    @Note("单例的 JsonFactory 工厂实例")
    private static final JsonFactory FACTORY = new JsonFactory();

    @Note("序列化时过滤 null 值的 ObjectMapper。Map 集合中 value 为 null 也会被过滤")
    protected static final ObjectMapper NOTNULL;

    @Note("允许序列化后的 JSON 字符串有 null 值的 ObjectMapper")
    protected static final ObjectMapper HASNULL;

    @Note("本工具类用于处理 [日期时间] 的格式化实例")
    public static final DateTimeFormatter DEFAULT_DATETIME_FORMATTER;

    @Note("本工具类用于处理 [日期] 的格式化实例")
    public static final DateTimeFormatter DEFAULT_DATE_FORMATTER;

    @Note("本工具类用于处理 [时间] 的格式化实例")
    public static final DateTimeFormatter DEFAULT_TIME_FORMATTER;

    /* *******************************************************************************************
     *
     *          初始化
     *
     * *******************************************************************************************
     * */

    static {
        //日期时间
        String datetime = (String) ProfileKit.get("json-kit-datetime-format");
        if(datetime == null || datetime.isEmpty()) {
            DEFAULT_DATETIME_FORMATTER = DateKit.DEFAULT_DATETIME_FORMATTER;
        } else if("timestamp".equalsIgnoreCase(datetime)) {
            DEFAULT_DATETIME_FORMATTER = null;
        } else {
            DEFAULT_DATETIME_FORMATTER = DateTimeFormatter.ofPattern(datetime);
        }
        //日期
        String date = (String) ProfileKit.get("json-kit-date-format");
        if(date == null || date.isEmpty()) {
            DEFAULT_DATE_FORMATTER = DateKit.DEFAULT_DATE_FORMATTER;
        } else if("timestamp".equalsIgnoreCase(date)) {
            DEFAULT_DATE_FORMATTER = null;
        } else {
            DEFAULT_DATE_FORMATTER = DateTimeFormatter.ofPattern(date);
        }
        //时间
        String time = (String) ProfileKit.get("json-kit-time-format");
        if(time == null || time.isEmpty()) {
            DEFAULT_TIME_FORMATTER = DateKit.DEFAULT_TIME_FORMATTER;
        } else if("timestamp".equalsIgnoreCase(time)) {
            DEFAULT_TIME_FORMATTER = null;
        } else {
            DEFAULT_TIME_FORMATTER = DateTimeFormatter.ofPattern(time);
        }
        NOTNULL = newFilterNullValueMapper();
        HASNULL = newHasNullValueMapper();
    }

    @Note("设置 JsonFactory 的 Codec")
    protected static void setCodeC(ObjectMapper mapper) {
        FACTORY.setCodec(mapper);
    }

    /* *******************************************************************************************
     *
     *          构建 ObjectMapper 对象
     *
     * *******************************************************************************************
     * */

    @Note("构建过滤 null 值的 ObjectMapper 对象")
    public static ObjectMapper newFilterNullValueMapper() {
        ObjectMapper mapper = new ObjectMapper();
        configMapperFilterNull(mapper);
        configMapperAccessAllField(mapper);
        configMapperSkipUnknownField(mapper);
        configMapperSingleValueAsArray(mapper);
        configMapperCharArrayToArray(mapper);
        configMapperRWByEnumToString(mapper);
        addCustomSerializers(mapper, SerializerHandler.all());
        addCustomDeserializers(mapper, DeserializerHandler.all());
        return mapper;
    }


    @Note("构建不过滤 null 值的 ObjectMapper 对象")
    public static ObjectMapper newHasNullValueMapper() {
        ObjectMapper mapper = new ObjectMapper();
        configMapperAccessAllField(mapper);
        configMapperSkipUnknownField(mapper);
        configMapperCharArrayToArray(mapper);
        configMapperSingleValueAsArray(mapper);
        configMapperRWByEnumToString(mapper);
        addCustomSerializers(mapper, SerializerHandler.all());
        addCustomDeserializers(mapper, DeserializerHandler.all());
        return mapper;
    }

    public static ObjectMapper getNOTNULL() {
        return NOTNULL;
    }

    public static ObjectMapper getHASNULL() {
        return HASNULL;
    }

    public static void addCustomSerializersToNOTNULLMapper(Map<Class, JsonSerializer> serializerMap) {
        addCustomSerializers(NOTNULL, serializerMap);
    }

    public static void addCustomSerializersToHASNULLMapper(Map<Class, JsonSerializer> serializerMap) {
        addCustomSerializers(HASNULL, serializerMap);
    }

    public static void addCustomDeserializersToNOTNULLMapper(Map<Class, StdDeserializer> deserializerMap) {
        addCustomDeserializers(NOTNULL, deserializerMap);
    }

    public static void addCustomDeserializersToHASNULLMapper(Map<Class, StdDeserializer> deserializerMap) {
        addCustomDeserializers(HASNULL, deserializerMap);
    }



    /* *******************************************************************************************
     *
     *          功能方法
     *
     * *******************************************************************************************
     * */

    @Note("添加自定义的序列化器到目标 ObjectMapper 里")
    public static void addCustomSerializers(ObjectMapper mapper, Map<Class, JsonSerializer> serializerMap) {
        SimpleModule module = new SimpleModule();
        for (Map.Entry<Class, JsonSerializer> e : serializerMap.entrySet()) {
            module.addSerializer(e.getKey(), e.getValue());
        }
        mapper.registerModule(module);
    }

    @Note("添加自定义的序列化器到目标 ObjectMapper 里")
    public static void addCustomDeserializers(ObjectMapper mapper, Map<Class, StdDeserializer> deserializerMap) {
        SimpleModule module = new SimpleModule();
        for (Map.Entry<Class, StdDeserializer> e : deserializerMap.entrySet()) {
            module.addDeserializer(e.getKey(), e.getValue());
        }
        mapper.registerModule(module);
    }


    @Note("读取 JSON 字符串为 JsonParser 解析器")
    public static JsonParser newJsonParser(String json) {
        try {
            JsonParser parser = FACTORY.createParser(json);
            parser.setCodec(NOTNULL);
            return parser;
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }

    @Note("读取 JSON 文件为 JsonParser 解析器")
    public static JsonParser newJsonParser(File json) {
        try {
            JsonParser parser = FACTORY.createParser(json);
            parser.setCodec(NOTNULL);
            return parser;
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }

    @Note("读取 JSON 字节流为 JsonParser 解析器")
    public static JsonParser newJsonParser(InputStream jsonInput) {
        try {
            JsonParser parser = FACTORY.createParser(jsonInput);
            parser.setCodec(NOTNULL);
            return parser;
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }

    @Note("读取 JSON 字符流为 JsonParser 解析器")
    public static JsonParser newJsonParser(Reader jsonReader) {
        try {
            JsonParser parser = FACTORY.createParser(jsonReader);
            parser.setCodec(NOTNULL);
            return parser;
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }

    @Note("读取 JSON URL 网络流为 JsonParser 解析器")
    public static JsonParser newJsonParser(URL url) {
        try {
            JsonParser parser = FACTORY.createParser(url);
            parser.setCodec(NOTNULL);
            return parser;
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }

    @Note("从 JsonParser 中获取 JsonNode 对象，常用于自定义反序列化")
    public static JsonNode readToNode(String json) {
        return readToNode(newJsonParser(json));
    }

    @Note("从 JsonParser 中获取 JsonNode 对象，常用于自定义反序列化")
    public static JsonNode readToNode(JsonParser parser) {
        try {
            ObjectCodec codec = parser.getCodec();
            return codec.readTree(parser);
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }


    @Note("关闭 JsonParser 流")
    public static void close(JsonParser parser) {
        if(parser != null) {
            try {
                parser.close();
            } catch (IOException e) {
                throw new IORuntimeException(e);
            }
        }
    }

    @Note("使用外部指定的 ObjectMapper 对象序列化指定的 JavaBean 对象为 JSON 字符串")
    public static String writeAsJson(ObjectMapper mapper, Object javaBean) {
        if(javaBean == null) return "";
        if((javaBean instanceof CharSequence) || BasicEnum.isBasicOrWrapper(javaBean.getClass())) {
            return javaBean.toString();
        }
        String json;
        try {
            json = mapper.writeValueAsString(javaBean);
        } catch (JsonProcessingException e) {
            throw new ParseRuntimeException(e, "对象 " + javaBean + "，类型 【"
                    + javaBean.getClass().getName() + "】 序列化 JSON 字符串失败");
        }
        return json;
    }

    @Note("把目标 JSON 字符串转化为对应字节码类型的 Java 对象，整个过程捕获异常")
    public static <T> T writeAsObject(ObjectMapper mapper, String json, Class<T> objClass) {
        T obj;
        try {
            obj = mapper.readValue(json, objClass);
        } catch (JsonProcessingException e) {
            String info = (objClass == null) ? null : objClass.getName();
            throw new ParseRuntimeException(e, "目标 JSON 字符串 【" + json + "】 无法转换为 Java 对象 【" + info + "】 ");
        }
        return obj;
    }

    /* *******************************************************************************************
     *
     *          对 ObjectMapper 对象做一些简单配置
     *
     * *******************************************************************************************
     * */

    @Note("注册 Module 模块到 ObjectMapper 中")
    public static void registerModule(ObjectMapper mapper, Module module) {
        if(mapper == null || module == null) return;
        mapper.registerModule(module);
    }

    @Note("配置入参的 ObjectMapper 对象可以忽略权限修饰符访问所有属性")
    public static void configMapperAccessAllField(ObjectMapper mapper) {
        if(mapper == null) return;
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    }

    @Note("设置入参的 ObjectMapper 对象过滤 null 值")
    public static void configMapperFilterNull(ObjectMapper mapper) {
        if(mapper == null) return;
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }

    @Note("设置入参的 ObjectMapper 对象在反序列化时跳过 JSON 字符串中有但 JavaBean 对象不存在的属性")
    public static void configMapperSkipUnknownField(ObjectMapper mapper) {
        if(mapper == null) return;
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    @Note("允许将单一值反序列化为数组。当遇到单一值时，将其解析为只包含该值的数组")
    public static void configMapperSingleValueAsArray(ObjectMapper mapper) {
        if(mapper == null) return;
        mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
    }

    @Note("序列化 char 数组时转为字符串数组，而不是单纯地转为字符串")
    public static void configMapperCharArrayToArray(ObjectMapper mapper) {
        if(mapper == null) return;
        mapper.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS, true);
    }

    @Note("设置入参的 ObjectMapper 对象在序列化和反序列化枚举类对象时根据其 toString 方法的结果来操作")
    public static void configMapperRWByEnumToString(ObjectMapper mapper) {
        if(mapper == null) return;
        mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
        mapper.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, false);
    }


    /* *******************************************************************************************
     *
     *          流模式
     *
     * *******************************************************************************************
     * */

    @Note("运用 Jackson 的流模式转换 JSON 字符串为 Java 对象。" +
            "其中对象类型转为 LinkedHashMap，数组类型转为 ArrayList")
    public static Object parse(String json) {
        return parse(newJsonParser(json));
    }

    @Note("运用 Jackson 的流模式转换 JSON 字符串为 Java 对象。" +
            "其中对象类型转为 LinkedHashMap，数组类型转为 ArrayList")
    public static Object parse(JsonParser parser) {
        try {
            if(parser.currentToken() == null) {
                parser.nextToken();
            }
            return parseToken(parser);
        } catch (Exception e) {
            throw new IORuntimeException(e);
        } finally {
            close(parser);
        }
    }


    @Note("解析 JsonToken 的类型，转为相对应的 value 返回")
    public static Object parseToken(JsonParser parser) throws IOException {
        JsonToken token = parser.currentToken();
        switch (token) {
            case START_OBJECT: return parseObjectToLinkedHashMap(parser);
            case START_ARRAY: return private_parseArrayToArrayList(parser);
            case VALUE_STRING: return parser.getValueAsString();
            case VALUE_NUMBER_INT: return ObjKit.stringToWholeNumber(parser.getValueAsString());
            case VALUE_NUMBER_FLOAT: return ObjKit.stringToFloatNumber(parser.getValueAsString());
            case VALUE_TRUE:
            case VALUE_FALSE: return BasicEnum.booleanValue(parser.getValueAsString());
            case VALUE_NULL: return null;
            default: throw new IllegalStateException("不知道 " + token + " 该转换为什么值");
        }
    }

    @Note("解析 JsonToken 的类型，转为指定类型的 value 返回")
    public static Object parseToken(JsonParser parser, C<?> targetType) throws IOException {
        JsonToken token = parser.currentToken();
        switch (token) {
            case START_OBJECT: return private_parseObject(parser, targetType);
            case START_ARRAY: return private_parseArray(parser, targetType);
            case VALUE_STRING:
            case VALUE_NUMBER_INT:
            case VALUE_NUMBER_FLOAT:
            case VALUE_TRUE:
            case VALUE_FALSE: return ObjKit.stringToRealValue(parser.getValueAsString(), targetType);
            case VALUE_NULL: return null;
            default: throw new IllegalStateException("不知道 " + token + " 该转换为什么值");
        }
    }

    @Note("解析当前的 JsonToken 与 value 转为 HashMap 集合返回")
    public static HashMap<String, Object> parseObjectToHashMap(JsonParser parser) throws IOException {
        HashMap<String, Object> map = new HashMap<>();
        mapParseToken(parser, map);
        return map;
    }

    @Note("解析当前的 JsonToken 与 value 转为 LinkedHashMap 集合返回")
    public static LinkedHashMap<String, Object> parseObjectToLinkedHashMap(JsonParser parser) throws IOException {
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        mapParseToken(parser, map);
        return map;
    }


    @Note("解析当前的 JsonToken 与 value 转为 ArrayList 集合返回")
    public static ArrayList<Object> parseArrayToArrayList(JsonParser parser, C<?> targetType) throws IOException {
        ArrayList<Object> list = new ArrayList<>();
        while (parser.nextToken() != JsonToken.END_ARRAY) {
            list.add(parseToken(parser, targetType));
        }
        return list;
    }

    @Note("解析 JsonToken 与 value，将得到的 key-value 拷贝至目标 Map 集合里")
    public static void mapParseToken(JsonParser parser, Map map) throws IOException {
        String fieldName;
        while (parser.nextToken() != JsonToken.END_OBJECT) {
            if (parser.currentToken() == JsonToken.FIELD_NAME) {
                fieldName = parser.getCurrentName();
                parser.nextToken();
                map.put(fieldName, parseToken(parser));
            }
        }
    }

    @Note("解析 JsonToken 与 value，将得到的 key-value 拷贝至目标自定义键值对集合里")
    public static void pairsParseToken(JsonParser parser, OwnPairs pairs) throws IOException {
        String fieldName;
        while (parser.nextToken() != JsonToken.END_OBJECT) {
            if (parser.currentToken() == JsonToken.FIELD_NAME) {
                fieldName = parser.getCurrentName();
                parser.nextToken();
                pairs.put(fieldName, parseToken(parser));
            }
        }
    }

    @Note("解析 JsonToken 与 value，将得到的 value 拷贝至目标 Collection 集合里")
    public static void collectionParseToken(JsonParser parser, Collection c) throws IOException {
        JsonToken nextToken;
        while ((nextToken = parser.nextToken()) != null && nextToken != JsonToken.END_ARRAY) {
            c.add(parseToken(parser));
        }
    }

    @Note("解析 JsonToken 与 value，将得到的 value 拷贝至目标自定义集合容器里")
    public static void ownRowParseToken(JsonParser parser, OwnRows r) throws IOException {
        JsonToken nextToken;
        while ((nextToken = parser.nextToken()) != null && nextToken != JsonToken.END_ARRAY) {
            r.add(parseToken(parser));
        }
    }

    @Note("转为 JsonNode 为基本类型的数据")
    public static Object jsonNodeToBasicValue(JsonNode node) {
        switch (node.getNodeType()) {
            case NUMBER: {
                if(node.isLong()) return node.asLong();
                if(node.isInt()) return node.asInt();
                return node.asDouble();
            }
            case STRING: return node.asText();
            case BOOLEAN: return node.asBoolean();
            default: return node;
        }
    }

    /* *******************************************************************************************
     *
     *          私有逻辑
     *
     * *******************************************************************************************
     * */

    @Note("解析当前的 JsonToken 与 value 转为对应的键值对集合或 JavaBean 对象返回")
    private static Object private_parseObject(JsonParser parser, C<?> targetType) {
        Class<?> mainType = targetType.getMainType();
        if(Map.class.isAssignableFrom(mainType)) {
            return MapTypeHandler.castToMap(parser, targetType);
        }
        if(OwnPairs.class.isAssignableFrom(mainType)) {
            return PairTypeHandler.castToOwnPairs(parser, targetType);
        }
        return ObjKit.buildObject(targetType, parser);
    }

    @Note("解析当前的 JsonToken 与 value 转为数组容器或集合容器返回")
    private static Object private_parseArray(JsonParser parser, C<?> targetType) {
        Class<?> mainType = targetType.getMainType();
        if(List.class.isAssignableFrom(mainType)) {
            return ListTypeHandler.castToList(parser, targetType);
        }
        if(Set.class.isAssignableFrom(mainType)) {
            return SetTypeHandler.castToSet(parser, targetType);
        }
        if(AbsOwnSet.class.isAssignableFrom(mainType)) {
            return SetTypeHandler.castToAbsOwnSet(parser, targetType);
        }
        if(OwnRows.class.equals(mainType) || AbsOwnList.class.isAssignableFrom(mainType)) {
            return ListTypeHandler.castToAbsOwnList(parser, targetType);
        }
        return ObjKit.buildObject(targetType, parser);
    }


    @Note("解析当前的 JsonToken 与 value 转为 ArrayList 集合返回")
    private static ArrayList<Object> private_parseArrayToArrayList(JsonParser parser) throws IOException {
        ArrayList<Object> list = new ArrayList<>();
        while (parser.nextToken() != JsonToken.END_ARRAY) {
            list.add(parseToken(parser));
        }
        return list;
    }

    @Note("转换当前值为数字类型")
    private static Number private_parseToNumber(JsonParser parser) throws IOException {
        String s = parser.getValueAsString();
        if(s.length() <= 10) {
            try {
                return Integer.parseInt(s);
            } catch (Exception e) {
                return Long.parseLong(s);
            }
        }
        return Long.parseLong(s);
    }




}
